/**
 * ModelDownloadFile.java
 */
package model;
import java.util.*;
import dataTypes.DownloadFile;
import dataTypes.Host;


/**
 * @author Michal Kolodziejski
 *
 */
public class ModelDownloadFile extends DownloadFile {
	private List<Host> hosts;
	private int nextHostIndex;
	private List<Integer> lackingParts;
	private int nextLackingPartIndex;
	
	/**
	 * Default constructor
	 */
	public ModelDownloadFile() {
		hosts = new ArrayList<Host>();
		nextHostIndex = 0;
		lackingParts = new ArrayList<Integer>();
		nextLackingPartIndex = 0;
	}

	/**
	 * Parametrized constructor
	 * @param name
	 * @param path
	 * @param size
	 * @param hash
	 */
	public ModelDownloadFile(String path, long size, String hash, DownloadStateType downloadState) {
		super(path, size, hash, downloadState, 0);
		hosts = new ArrayList<Host>();
		nextHostIndex = 0;
		lackingParts = new ArrayList<Integer>();
		nextLackingPartIndex = 0;
		// fill lackingParts list
		fillAllLackingParts();
	}
	
	
	// overriden method which also fills lackingParts list 
	public void setSize(long size){
		super.setSize(size);
	}
	
	/**
	 * Adds host to the list of hosts serving this file
	 * @param host a host to be added
	 */
	public void addHost(Host host){
		if(hosts.contains(host))
			return;
		this.hosts.add(host);
	}
	
	/**
	 * Removes host from the list
	 * @param host the host to be removed from the list
	 */
	public void removeHost(Host host){
		if(hosts.indexOf(host) != -1 && hosts.indexOf(host) < nextHostIndex)
			--nextHostIndex;
		this.hosts.remove(host);	
	}
	
	/**
	 * Returns list of hosts serving this file
	 * @return list of hosts serving this file
	 */
	public List<Host> getHostsList(){
		return this.hosts;
	}
	
	/**
	 * Iterates circularily over the hosts serving this file and returns next to last returned.
	 * @return next host or <b>null</b> if none found
	 */
	public Host getNextHost(){
		if(hosts.size()==0)
			return null;
		
		if(nextHostIndex>=hosts.size()){
			nextHostIndex = 0;
		}
		
		return hosts.get(nextHostIndex++);
	}
	
	/**
	 * Removes lacking part from the list (that is: marks the part as present)
	 * @param part
	 */
	public void removeLackingPart(int part){
		Integer p = new Integer(part);
		if(lackingParts.indexOf(p) != -1 && lackingParts.indexOf(p) < nextLackingPartIndex)
			--nextLackingPartIndex;
		
		lackingParts.remove(p);
		++downloadedPartsNumber;
	}
	
	/**
	 * Iterates circularily over the list of lacking parts of this file and returns next to last returned.
	 * @return next lacking part or <b>-1</b> if none found
	 */
	public int getNextLackingPart(){
		if(lackingParts.size()==0)
			return -1;
		
		if(nextLackingPartIndex >= lackingParts.size())
			nextLackingPartIndex = 0;
		
		return lackingParts.get(nextLackingPartIndex++);
	}
	
	
	/**
	 * Returns number of already downloaded parts before specified part.
	 * @param partNo limiting part
	 * @return number of downloaded parts
	 */
	public int getDownloadedPartsNumberBefore(int partNo){
		int result = 0;
		for(ListIterator<Integer> it = lackingParts.listIterator(); it.hasNext();){
			Integer i = it.next();
			if(i.intValue()<partNo){
				++result;
			}
			else
				break;
		}
		result = partNo - result;
		return result;
	}
	
	/**
	 * Checks if list of hosts is empty.
	 * @return <b>true</b> if list of hosts is empty, <b>false</b> otherwise
	 */
	public boolean isHostsListEmpty(){
		return hosts.isEmpty();
	}
	
	/**
	 * Checks if list of lacking parts is empty.
	 * @return <b>true</b> if list of lacking parts is empty, <b>false</b> otherwise
	 */
	public boolean isLackingPartsListEmpty(){
		return lackingParts.isEmpty();
	}
	
	
	public void clearHostsList(){
		hosts.clear();
		nextHostIndex = 0;;
	}
	

	public boolean isLacking(int i){
		return lackingParts.contains(new Integer(i));
	}
	
	public int getLackingPartsNumber(){
		return lackingParts.size();
	}
	
	/* ************ REQUIRED BY XMLDecoder / XMLEncoder **************/

	/**
	 * @return the hosts
	 */
	public List<Host> getHosts() {
		return hosts;
	}

	/**
	 * @return the lackingParts
	 */
	public List<Integer> getLackingParts() {
		return lackingParts;
	}

	/**
	 * @param hosts the hosts to set
	 */
	public void setHosts(List<Host> hosts) {
		this.hosts = hosts;
	}

	/**
	 * @param lackingParts the lackingParts to set
	 */
	public void setLackingParts(List<Integer> lackingParts) {
		this.lackingParts = lackingParts;
	}
	
	public void setDownloadedPartsNumber(int downloadedPartsNumber){
		this.downloadedPartsNumber = downloadedPartsNumber;
	}

	
	
	/* *****************************
	 *		PRIVATE METHODS 
	 */
	
	public void fillAllLackingParts(){
		lackingParts.clear();
		for(int i=0; i<getNumberOfParts(); ++i)
			lackingParts.add(i);
		nextLackingPartIndex = 0;
		downloadedPartsNumber = 0;
	}

}
